<?php
namespace hema\wechat;

use app\common\model\Applet;
use app\common\model\Setting;
use app\common\model\Record;
use app\common\model\DivideAccount;
use think\facade\Cache;

/**
 * 微信支付
 */
class Pay
{
    private $config; // 微信支付参数

    /**
     * 构造方法
     */
    public function __construct(array $config = [])
    {
        $this->config = $config;
    }

    /**
     * 统一下单API
     * 参数 $trade_type=支付类型（JSAPI=小程序,NATIVE=扫码支付，MICROPAY=付款码）$order_no=订单号 $openid=微信用户ID, $total_fee=支付金额, ,$attach=订单描述 $divide=是否分账 $auth_code=付款码
     */
    public function unifiedorder($trade_type,$order_no, $total_fee, string $notify_url, string $openid='',$attach = '订单支付', $divide = false, $auth_code='')
    {
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonceStr = md5($time . $openid);
		// API参数
		$params = [
		    'trade_type' => $trade_type,
			'attach' => $attach,
			'nonce_str' => $nonceStr,//随机字符串
			'body' => $attach,//商品描述
			'out_trade_no' => $order_no,//商户订单号
			'total_fee' => $total_fee * 100, // 价格:单位分
			'spbill_create_ip' => \request()->ip(),//服务终端IP
		];
        if($trade_type == 'MICROPAY'){
            $params['auth_code'] = $auth_code; //付款码支付
            $url = 'https://api.mch.weixin.qq.com/pay/micropay';// 请求API
        }else{
            $params['notify_url'] = base_url() . $notify_url; // 异步通知地址	
            $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        }
		if($this->config['is_sub'] == 1){
            //服务商统一下单
            $values = Setting::getItem('wxpayisp',0);
			$this->config['api_key'] = $values['api_key'];//服务商商户的密钥
			$params['appid'] = $values['app_id'];//服务商商户的APPID
			$params['mch_id'] = $values['mch_id'];//服务商商户号
			$params['sub_appid'] = $this->config['app_id'];//当前调起支付的小程序APPID
			$params['sub_mch_id'] = $this->config['mch_id'];//服务商分配的子商户号
			!empty($openid) && $params['sub_openid'] = $openid;//下单用户标识
		}else{
			$params['appid'] = $this->config['app_id'];//小程序ID
			$params['mch_id'] = $this->config['mch_id'];//商户号
			!empty($openid) && $params['openid'] = $openid;//下单用户标识
		}
        if($trade_type != 'NATIVE'){
            //判断是否开启分账
            if($divide){
                $params['profit_sharing'] = 'Y';//开启分账
            }else{
                $webpay = Setting::getItem('webpay',0);
                //判断是否开启统一分佣
                if($webpay['divide']['mode'] == 'unify' AND $webpay['divide']['extract'] > 0){
                    $params['profit_sharing'] = 'Y';//开启分账
                }
                if($webpay['divide']['mode'] == 'order'){
                    if($webpay['divide']['order']['tang'] > 0 OR $webpay['divide']['order']['waimai'] > 0 OR $webpay['divide']['order']['ziqu'] > 0){
                         $params['profit_sharing'] = 'Y';//开启分账
                    }
                }
            }
        }		
        // 生成签名
        $params['sign'] = $this->makeSign($params);
        $result = $this->postXmlCurl($this->toXml($params), $url);
        $prepay = $this->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            die(hema_json(['code' => -10, 'msg' => $prepay['return_msg']]));
        }
        //判断付款码支付时，用户支付中，需要输入密码
        if ($trade_type == 'MICROPAY' AND $prepay['result_code'] === 'USERPAYING') {
            return false;
        }
        if ($prepay['result_code'] === 'FAIL') {
            die(hema_json(['code' => -10, 'msg' => $prepay['err_code_des']]));
        }
        //付款码支付
		if($trade_type == 'MICROPAY' AND $prepay['trade_type'] == 'MICROPAY'){
			return $prepay['transaction_id'];//支付交易号
		}
		//WEB扫码支付
		if(empty($openid) AND $trade_type == 'NATIVE'){
			return $prepay['code_url'];//返回收款二维码 
		}
        // 生成 nonce_str 供前端使用
        $paySign = $this->makePaySign($params['nonce_str'], $prepay['prepay_id'], $time);
		//小程序支付
		return [
            'prepay_id' => $prepay['prepay_id'],
            'nonceStr' => $nonceStr,
            'timeStamp' => (string)$time,
            'paySign' => $paySign
        ];
    }
    
    /**
     * 查询付款码支付结果是否成功
     */
    public function orderquery($out_trade_no)
    {
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonceStr = md5($time);
		// API参数
		$params = [
		    'out_trade_no' => $out_trade_no,
			'nonce_str' => $nonceStr,//随机字符串
		];
		if($this->config['is_sub'] == 1){
            //服务商统一下单
            $values = Setting::getItem('wxpayisp',0);
			$this->config['api_key'] = $values['api_key'];//服务商商户的密钥
			$params['appid'] = $values['app_id'];//服务商商户的APPID
			$params['mch_id'] = $values['mch_id'];//服务商商户号
			$params['sub_appid'] = $this->config['app_id'];//当前调起支付的小程序APPID
			$params['sub_mch_id'] = $this->config['mch_id'];//服务商分配的子商户号
		}else{
			$params['appid'] = $this->config['app_id'];//小程序ID
			$params['mch_id'] = $this->config['mch_id'];//商户号
		}		
        // 生成签名
        $params['sign'] = $this->makeSign($params);
        // 请求API
        $url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        $result = $this->postXmlCurl($this->toXml($params), $url);
        $prepay = $this->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'SUCCESS' AND $prepay['result_code'] === 'SUCCESS') {
            return $prepay['trade_state'];
        }
        return 'ERROR';
    }

    /**
     * 支付成功异步通知
     */
    public function notify($Model,$method='edit',$api_key='')
    {
        //接收微信服务器回调的数据流
        if (!$xml = file_get_contents('php://input')) {
            $this->returnCode(false, 'Not found DATA');
        }
        // 将服务器返回的XML数据转化为数组
        $data = $this->fromXml($xml);
        // 订单信息
        $order = $Model->payDetail($data['out_trade_no']);
        empty($order) && $this->returnCode(true, '订单不存在');
        if(empty($api_key)){
            if(isset($data['sub_appid'])){
                $isp = Setting::getItem('wxpayisp',0);
                $this->config = $isp;//服务商支付的密钥
            }else{
                $this->config = Setting::getItem('wxpay',$order['applet_id']);//商户支付的密钥
            }
        }else{
            $this->config['api_key'] = $api_key;
        }
        // 保存微信服务器返回的签名sign
        $dataSign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        // 生成签名
        $sign = $this->makeSign($data);
        // 判断签名是否正确  判断支付状态
        if (($sign === $dataSign)
            && ($data['return_code'] == 'SUCCESS')
            && ($data['result_code'] == 'SUCCESS')) {
            if($method == 'add'){
                $Model->updatePayStatus($data['transaction_id'],$order);
                Cache::delete($data['out_trade_no']);
            }else{
                // 更新订单状态
                $order->updatePayStatus($data['transaction_id']);
            }
            // 返回状态
            $this->returnCode(true, 'OK');
        }
        // 返回状态
        $this->returnCode(false, '签名失败');
    }
    
    /**
     * 完结分账
     */
    public function divide($order)
    {
        if(empty($order['transaction_id'])){
            return true;//不支持余额分账
        }
		$is_divide = false;//是否分账
		$divide_price = [
		   'service_fee' => 0,//交易流水分账初始金额
		   'delivery_fee' => 0,//配送费分账初始金额
		   'agent_fee' => 0, //代理分账初始金额
		];
		$agent_openid = '';//代理收款账号
		$webpay = Setting::getItem('webpay',0); //获取站点支付参数
		//如果开启 统一分佣模式
        if($webpay['divide']['mode'] == 'unify' AND $webpay['divide']['extract'] > 0){
            $extract = $webpay['divide']['extract'];//获取抽取比例
            $is_divide = true; //开启分账模式
        }
        //如果开启 订单分佣模式
        if($webpay['divide']['mode'] == 'order'){
            //堂食模式
            if($order['order_mode']['value'] == 10 AND $webpay['divide']['order']['tang'] > 0){
                $extract = $webpay['divide']['order']['tang'];//获取抽取比例
                $is_divide = true; //开启分账模式
            }
            //外卖模式
            if($order['order_mode']['value'] == 20 AND $webpay['divide']['order']['waimai'] > 0){
                $extract = $webpay['divide']['order']['waimai'];//获取抽取比例
                $is_divide = true; //开启分账模式
            }
            //自取食模式
            if($order['order_mode']['value'] == 30 AND $webpay['divide']['order']['ziqu'] > 0){
                $extract = $webpay['divide']['order']['ziqu'];//获取抽取比例
                $is_divide = true; //开启分账模式
            }
        }
        //如果开启了佣金分账 - 计算分佣总额
        if($is_divide){
            $service_fee = $order['pay_price'];//获取订单总金额
            //判断是否包含配送费
            if($webpay['divide']['mode'] != 1){
                $service_fee = $service_fee - $order['express_price'];//分佣总金额扣除配送费
            }
            $divide_price['service_fee'] = $service_fee * $extract; //获得最终抽佣金额（单位分）
            //判断代理是否抽佣
            if($webpay['divide']['agent_fee'] > 0){
                $applet = Applet::get($order['applet_id']);//获取商家应用
                //判断商家是否有代理商
                if($applet['agent_id'] > 0){
                    if($account = DivideAccount::withoutGlobalScope()->where('applet_id',$order['applet_id'])->find()){
                       $agent_openid = $account['open_id']; 
                    }
                    $divide_price['agent_fee'] = $divide_price['service_fee'] * $webpay['divide']['agent_fee'] / 100;//计算代理分佣金额（单位分）
                    $divide_price['service_fee'] = $divide_price['service_fee'] - $divide_price['agent_fee'];//重算平台获取佣金金额 - 扣除分佣给代理的金额 
                }
            }
        }
        //判断外卖订单是否分账配送费
        if($order['order_mode']['value'] == 20){
            if($dv_company = get_addons_info($order['delivery']['company']['value'])){
                if($dv_company['status'] == 1){
                    if($dv_config = get_addons_config($dv_company['name'])){
                        if($dv_config['pay_mode'] == 1){
                            $divide_price['delivery_fee'] = $order['delivery']['price'] * 100; //单位分
                        }
                    }
                }
            }
            if($divide_price['delivery_fee'] > 0){
                $is_divide = true; //配送费大于0 开启分账
            }
        }
        //********************* 分账 - 开始 *************************//
        if($is_divide){
            //组合支付参数
            if($this->config['is_sub'] == 1){
                //服务商统一下单
                $wxpayisp = Setting::getItem('wxpayisp',0);
    			$this->config['api_key'] = $wxpayisp['api_key'];//服务商商户的密钥
    			$this->config['cert_pem'] = $wxpayisp['cert_pem'];//证书
                $this->config['key_pem'] = $wxpayisp['key_pem'];//证书密钥
    			$wxpay['appid'] = $wxpayisp['app_id'];//服务商商户的APPID
    			$wxpay['mch_id'] = $wxpayisp['mch_id'];//服务商商户号
    			$wxpay['sub_appid'] = $this->config['app_id'];//当前调起支付的小程序APPID
    			$wxpay['sub_mch_id'] = $this->config['mch_id'];//服务商分配的子商户号
    		}else{
    			$wxpay['appid'] = $this->config['app_id'];//小程序ID
    			$wxpay['mch_id'] = $this->config['mch_id'];//商户号
    		}
            //***************** 添加分账接收方 - 开始 *********************//
            $params = $wxpay;
    		//如果平台佣金 大于 0
    		if($divide_price['service_fee'] > 0){
    		    $params['nonce_str'] = md5(time() . 'hema_self');//随机字符串
                $params['receiver'] = json_encode([
                    'type' => 'MERCHANT_ID',//商户号
                    'account' => $webpay['wx']['mch_id'],
                    'name' => $webpay['wx']['name'],
                    'relation_type' => 'SERVICE_PROVIDER'
                ],JSON_UNESCAPED_UNICODE);//分账接收方
                $params['sign'] = $this->makeSignSha($params);// 生成签名
                $url = 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver';// 请求API
                $result = $this->postXmlCurl($this->toXml($params), $url);
                $prepay = $this->fromXml($result);
                if($prepay['return_code'] != 'SUCCESS' AND $prepay['result_code'] != 'SUCCESS') {
                    //添加分账接收方失败
                    //write_log('order_id='.$order['order_id'].',添加平台分账接收方失败',__DIR__);
                    return true;
                }
                
    		}
    		//如果代理佣金 大于 0
    		if($divide_price['agent_fee'] > 0){
    		    //如果代理收款账户不为空
    		    if(!empty($agent_openid)){
    		        $params['nonce_str'] = md5(time() . 'hema_self');//随机字符串
    		        if($this->config['is_sub'] == 1){
    		            $open_type = 'PERSONAL_SUB_OPENID';
    		        }else{
    		            $open_type = 'PERSONAL_OPENID';
    		        }
                    $params['receiver'] = json_encode([
                        'type' => $open_type,//个人openid
                        'account' => $agent_openid,
                        'relation_type' => 'DISTRIBUTOR'
                    ],JSON_UNESCAPED_UNICODE);//分账接收方
                    $params['sign'] = $this->makeSignSha($params);// 生成签名
                    $url = 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver';// 请求API
                    $result = $this->postXmlCurl($this->toXml($params), $url);
                    $prepay = $this->fromXml($result);
                    if($prepay['return_code'] != 'SUCCESS' AND $prepay['result_code'] != 'SUCCESS') {
                        //添加分账接收方失败
                        //write_log('order_id='.$order['order_id'].',添加代理分账接收方失败',__DIR__);
                        return true;
                    }
    		    }else{
    		        //否则，代理不参与分佣
    		        $divide_price['service_fee'] = $divide_price['service_fee'] + $divide_price['agent_fee'];//代理分佣金额返还给平台
    		        $divide_price['agent_fee'] = 0;//代理分佣金额清零
    		    }
    		}
            //***************** 请求单次分账 - 开始 *********************//
            $params = $wxpay;
            $params['transaction_id'] = $order['transaction_id'];
            $params['out_order_no'] = $order['order_no'];
            $receivers = [];//接收方信息
    		//如果平台佣金 大于 0
    		if($divide_price['service_fee'] > 0){
    		    $amount = (int)$divide_price['service_fee'] + (int)$divide_price['delivery_fee'];//计算分账金额
    		    array_push($receivers,[
                    'type' => 'MERCHANT_ID',//分账接收方类型 - 商户号
                    'account' => $webpay['wx']['mch_id'],//分账接收方账号
                    'amount' =>  $amount,//分账金额，单位为分
                    'description' => '分佣给平台', //分账描述
                ]);
    		}
    		//如果代理佣金 大于 0
    		if($divide_price['agent_fee'] > 0){
    		    if($this->config['is_sub'] == 1){
		            $open_type = 'PERSONAL_SUB_OPENID';
		        }else{
		            $open_type = 'PERSONAL_OPENID';
		        }
    		    array_push($receivers,[
                    'type' => $open_type,//分账接收方类型 - openid
                    'account' => $agent_openid,//分账接收方账号
                    'amount' =>  (int)$divide_price['agent_fee'],//分账金额，单位为分
                    'description' => '分佣给代理', //分账描述
                ]);
    		}
            $params['receivers'] = json_encode($receivers,JSON_UNESCAPED_UNICODE);
            $params['nonce_str'] = md5(time() . 'hema_self');//随机字符串
            $params['sign'] = $this->makeSignSha($params);// 生成签名
            $url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing';// 请求API
            $result = $this->postXmlCurl($this->toXml($params), $url,true);
            $prepay = $this->fromXml($result);
            //如果分请求成功
            if($prepay['return_code'] == 'SUCCESS' AND $prepay['result_code'] == 'SUCCESS') {
                $record_log = [];//交易流水记录
                //是否增加平台分红记录（分佣）
                if($divide_price['service_fee'] > 0){
                    $money = sprintf("%.2f",((int)$divide_price['service_fee'] + (int)$divide_price['agent_fee']) / 100);//计算金额
                    //平台分红（分佣）记录
                    array_push($record_log,[
                        'user_type' => 40, //平台
                        'action' => 50, //分红
                        'order_no' => $order['order_no'],
                        'money' => $money,
                        'remark' => '交易分佣'
                    ]);
                    //商户扣费记录
                    array_push($record_log,[
                        'user_type' => 20,//用户类型(10商家会员 20站点会员 30代理 40平台)
                        'action' => 20, //扣减
                        'order_no' => $order['order_no'],
                        'money' => $money,
                        'user_id' => $applet['user_id'],
                        'remark' => '交易服务费'
                    ]);
                }
                //是否增加配送费记录
                if($divide_price['delivery_fee'] > 0){
                    $money = sprintf("%.2f",(int)$divide_price['delivery_fee'] / 100);//计算金额
                    //平台收取记录
                    array_push($record_log,[
                        'user_type' => 40, //平台
                        'action' => 50, //分红
                        'order_no' => $order['order_no'],
                        'money' => $money,
                        'remark' => '第三方配送费'
                    ]);
                    //商户扣费记录
                    array_push($record_log,[
                        'user_type' => 20,//用户类型(10商家会员 20站点会员 30代理 40平台)
                        'action' => 20, //扣减
                        'order_no' => $order['order_no'],
                        'money' => $money,
                        'user_id' => $applet['user_id'],
                        'remark' => '第三方配送费'
                    ]);
                }
                //是否增加代理分佣记录
                if($divide_price['agent_fee'] > 0){
                    $money = sprintf("%.2f",(int)$divide_price['agent_fee'] / 100);//计算金额
                    //平台收取记录
                    array_push($record_log,[
                        'user_type' => 30, //代理
                        'action' => 50, //分红
                        'order_no' => $order['order_no'],
                        'money' => $money,
                        'user_id' => $applet['agent_id'],
                        'remark' => '交易分佣'
                    ]);
                }
                //批量增加交易记录
                $model = new Record;
                $model->saveAll($record_log);
                //开始完结分账
                $params = $wxpay;
                $params['nonce_str'] = md5(time() . 'hema_self');//随机字符串
        		$params['transaction_id'] = $order['transaction_id'];//微信订单号
        		$params['out_order_no'] = order_no();//单号
        		$params['description'] = '分账已完成';//分账完结描述
                $params['sign'] = $this->makeSignSha($params);// 生成签名
                $url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish';// 请求API
                $result = $this->postXmlCurl($this->toXml($params), $url,true);
                $prepay = $this->fromXml($result);
                //请求成功
                if ($prepay['return_code'] == 'SUCCESS' AND $prepay['result_code'] == 'SUCCESS') {
                    //$prepay['order_id'];//微信分账单号
                    return true;
                }
            }
            return true;
        }
        return true;
    }

    /**
     * 退款申请API
     */
    public function refund($refund_no,$transaction_id,$total_fee,$refund_fee,$refund_desc,$notify_url)
    {
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonceStr = md5($time);
        // API参数
        $params = [
            'nonce_str' => $nonceStr,//随机字符串
            'transaction_id' => $transaction_id,//微信支付订单号
            'out_refund_no' => $refund_no,//退款订单号
            'total_fee' => $total_fee * 100, // 订单总金额，价格:单位分
            'refund_fee' => $refund_fee * 100, // 退款总金额，价格:单位分
            'refund_desc' => $refund_desc,//退款原因
            'notify_url' => base_url() . $notify_url  // 异步通知地址
        ];
        if($this->config['is_sub'] == 1){
            //服务商统一下单
            $values = Setting::getItem('wxpayisp',0);
            $this->config['api_key'] = $values['api_key'];//服务商商户的密钥
            $this->config['cert_pem'] = $values['cert_pem'];//证书
            $this->config['key_pem'] = $values['key_pem'];//证书密钥
            $params['appid'] = $values['app_id'];//服务商商户的APPID
            $params['mch_id'] = $values['mch_id'];//服务商商户号
            $params['sub_appid'] = $this->config['app_id'];//当前调起支付的小程序APPID
            $params['sub_mch_id'] = $this->config['mch_id'];//服务商分配的子商户号
            
        }else{
            $params['appid'] = $this->config['app_id'];//小程序ID
            $params['mch_id'] = $this->config['mch_id'];//商户号
        }
        //判断证书是否存在
        if(empty($this->config['cert_pem']) OR empty($this->config['key_pem'])){
            die(json_encode(['code' => 0, 'msg' => '未配置证书']));
        }
        // 生成签名
        $params['sign'] = $this->makeSign($params);
        // 请求API
        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
        $result = $this->postXmlCurl($this->toXml($params), $url,true);
        $prepay = $this->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            return $prepay['return_msg'];
        }
        if ($prepay['result_code'] === 'FAIL') {
            return $prepay['err_code_des'];
        }
        return false;
    }

    /**
    * 退款成功异步通知
    */
    public function notifyRefund($Model)
    {
        //接收微信服务器回调的数据流
        if (!$xml = file_get_contents('php://input')) {
            $this->returnCode(false, 'Not found DATA');
        }
        // 将服务器返回的XML数据转化为数组
        $data = $this->fromXml($xml);
        if($data['return_code'] != 'SUCCESS'){
            return false;
        }
        if(isset($data['sub_appid'])){
            $this->config = Setting::getItem('wxpayisp',0);
        }else{
            $applet = Applet::getApplet(['app_id' => $data['appid']]);
            $this->config = Setting::getItem('wxpay',$applet['applet_id']);
        }
        //解密数据
        $data = $this->refund_decrypt($data['req_info']);
        $data = $this->fromXml($data);
        // 订单信息
        $order = $Model->refundDetail($data['out_refund_no']);
        empty($order) && $this->returnCode(false, '订单不存在');
        if ($data['refund_status'] == 'SUCCESS') {
            // 更新订单状态
            $order->updateRefundStatus($data['refund_id']);
            // 返回状态
            $this->returnCode(true, '退款成功');
        }
        // 返回状态
        $this->returnCode(false, '退款失败');
    }

    /**
     * 企业转账到零钱API
     */
    public function transfers($open_id,$order_no,$price,$desc='用户提现')
    {
        // 生成随机字符串
        $nonceStr = md5(time());
        // API参数
        $params = [
            'openid' => $open_id,//用户openid
            'partner_trade_no' => $order_no, // 商户订单号
            'amount' => $price * 100, // 转账金额
            'mch_appid' => $this->config['app_id'],//商户账号appid    
            'mchid' => $this->config['mchid'],//商户号
            'check_name' => 'NO_CHECK',  // 校验用户姓名 NO_CHECK：不校验真实姓名 FORCE_CHECK：强校验真实姓名
            'nonce_str' => $nonceStr,//随机字符串
            'desc' => $desc
        ];
        // 生成签名
        $params['sign'] = $this->makeSign($params);
        // 请求API
        $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
        $result = $this->postXmlCurl($this->toXml($params), $url,true);
        $prepay = $this->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            return [
                'code' => 1,
                'msg' => $prepay['return_msg']
            ];
        }
        if ($prepay['result_code'] === 'FAIL') {
            return [
                'code' => 1,
                'msg' => $prepay['err_code_des']
            ];
        }
        return [
            'code' => 0,
            'msg' => 'ok',
            'data' => $prepay
        ];
    }

    /*
     * 退款通知解密
     */
    private function refund_decrypt($req_info) 
    {
        $key = strtolower(md5($this->config['api_key']));
        return openssl_decrypt($req_info, "AES-256-ECB", $key);
    }

    /**
     * 返回状态给微信服务器
     */
    private function returnCode($is_success = true, $msg = null)
    {
        $xml_post = $this->toXml([
            'return_code' => $is_success ? 'SUCCESS' : 'FAIL',
            'return_msg' => $is_success ? 'OK' : $msg,
        ]);
        die($xml_post);
    }

    /**
     * 生成paySign
     */
    private function makePaySign($nonceStr, $prepay_id, $timeStamp)
    {
        $data = [
            'appId' => $this->config['app_id'],
            'nonceStr' => $nonceStr,
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'MD5',
            'timeStamp' => $timeStamp,
        ];
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->toUrlParams($data);
        //签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->config['api_key'];
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 将xml转为array
     */
    private function fromXml($xml)
    {
        // 禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 以post方式提交xml到对应的接口url
     */
    private function postXmlCurl($xml, $url, $cert = false, $second = 30)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);// 设置超时时间
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);//严格校验
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);// 要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_POST, TRUE);// post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        curl_setopt($ch, CURLOPT_HEADER, FALSE);// 是否返回请求头
        //判断是否使用证书
        if($cert){
            $path = root_path() . '/extend/hema/wechat/cert/';
            file_put_contents($path . 'apiclient_cert.pem',$this->config['cert_pem']);
            file_put_contents($path . 'apiclient_key.pem',$this->config['key_pem']);
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT,$path . 'apiclient_cert.pem');
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY,$path . 'apiclient_key.pem');
        }
        $data = curl_exec($ch);// 运行curl
        curl_close($ch);
        return $data;
    }

    /**
     * 生成签名MD5
     */
    private function makeSign($values)
    {
        //签名步骤一：按字典序排序参数
        ksort($values);
        $string = $this->toUrlParams($values);
        //签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->config['api_key'];
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
    
    /**
     * 生成签名SHA
     */
    private function makeSignSha($values)
    {
        //签名步骤一：按字典序排序参数
        ksort($values);
        $string = $this->toUrlParams($values);
        //签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->config['api_key'];
        //签名步骤三：MD5加密
        $string = hash_hmac("sha256",$string,$this->config['api_key']);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 格式化参数格式化成url参数
     */
    private function toUrlParams($values)
    {
        $buff = '';
        foreach ($values as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $buff .= $k . '=' . $v . '&';
            }
        }
        return trim($buff, '&');
    }

    /**
     * 输出xml字符
     */
    private function toXml($values)
    {
        if (!is_array($values)
            || count($values) <= 0
        ) {
            return false;
        }

        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

}
